#include <math.h>
#include "global.h"
#include "powerup.h"
#include "ship.h"
#include "projectile.h"
#include "asteroid.h"
#include "blackhole.h"

int main() {
	init();
    install_timer();
    LOCK_VARIABLE(ticks);
    LOCK_FUNCTION(ticker);
    install_int_ex(ticker, BPS_TO_TIMER(updates_per_second));
    blackhole hole;
	ship player;
    asteroid* roid;
    asteroid* roids[max_asteroids];

    projectile* bullet;
    projectile* laser;
	projectile* spread[3];

	int pressDelay = 0;

//----------------------------create projectiles-------------------------------------

    bullet  =  new projectile(player.get_nx(), player.get_ny(), proj_spd, player.get_theta(), player.get_distance(), 0);
    for(int i=0;i<3;i++){
        spread[i] = new projectile(player.get_nx(), player.get_ny(), proj_spd, player.get_theta(), player.get_distance(), 1);
    }
    laser  =  new projectile(player.get_nx(), player.get_ny(), proj_spd, player.get_theta(), player.get_distance(), 2);

//--------------------------------CREATE ASTEROIDS-----------------------------------
    for(int i=0;i<max_asteroids;i++){
        roids[i] = new asteroid();
    }
//-------------------------------create powerup----------------------------
    powerup *pow = new powerup();
    pow->set_type(0);


//MAIN GAME LOOP
    while ( (!key[KEY_ESC]) && (!game_over)) {
        while(ticks == 0){
            rest(100 / updates_per_second);//rest until a full tick has passed
        }
        while(ticks > 0){
            int old_ticks = ticks;

            player.move();
            player.set_cur_weapon();

            for(int i=0; i<max_asteroids; i++){
                roids[i]->move(&player, &hole);
            }
            pow->move(&player, &hole);
//--------------------------------------------------------------------------------------------
            if ( !keypressed() ) { pressDelay = 0; }
            if(keypressed){
                if ( pressDelay == 0 ) {
                    if((player.get_cur_proj() == 1 || player.get_cur_proj() == 2) && key[KEY_PLUS_PAD]){
                        player.set_proj_count(player.get_cur_proj(), 1);
                    }
                    pressDelay = 10;
                }
            }
            if (pressDelay != 0 ) pressDelay--;

            if(!bullet_fired){
                if( (mouse_b & 1) || (key[KEY_R]) ){
                    player.shoot(buffer, &player, bullet, spread, laser);
                }
                bullet->set_power(bulletpower);
            }
            if(bullet->get_exists()){
                bullet->move(roids, pow);
            }

            if(laser->get_exists()){
                laser->move(roids, pow);
            }

            for(int i=0;i<3;i++){
                if(!spread[i]->get_exists()){
                    spread[i]->set_power(spreadpower);
                }
                if(spread[i]->get_exists()){
                    spread[i]->move(roids, pow);
                }
            }

            ticks--;
            if(old_ticks <= ticks)//logic is taking too long: abort and draw a frame
            break;
        }
        draw_screen(buffer, player, *bullet, spread, *laser, roids, hole, pow, score);
        if(player.get_lives() == 0 || player.get_energy() == 5) {
            game_over = 1;
            if(player.get_lives() == 0){
                lose = 1;
            } else if (player.get_energy() == 5){
                win = 1;
            }
        }
    }
    while(!key[KEY_ESC]){
        if(win){
            textout_ex(buffer, font, "YOU SURVIVED! :)", SCREEN_W/2, SCREEN_H/2, GREEN, -1);
            textprintf_ex(buffer, font, SCREEN_W/2, SCREEN_H/2 + 50, WHITE, -1, "Score: %d", score);
            blit(buffer, screen, 0, 0, 0, 0, buffer->w, buffer->h);
        }
        else if(lose){
            textout_ex(buffer, font, "YOU DIED! :(", SCREEN_W/2, SCREEN_H/2, RED, -1);
            textprintf_ex(buffer, font, SCREEN_W/2, SCREEN_H/2 + 50, WHITE, -1, "Score: %d", score);
            blit(buffer, screen, 0, 0, 0, 0, buffer->w, buffer->h);;
        }
    }
    deinit();
    return 0;
}
END_OF_MAIN()

//ASTEROID-----------------------------------

asteroid::asteroid(){

    int r;
    r = rand()%4;
    switch(r){
        case 0: this->y = 0;
        break;
        case 1: this->x = SCREEN_W;
        break;
        case 2: this->y = SCREEN_H;
        break;
        case 3: this->x = 0;
        break;
    }
    if((r == 0) || (r == 2)){
        this->x = rand()%SCREEN_W;
    }
    else if((r == 1) || (r == 3)){
        this->y = rand()%SCREEN_H;
    }
    int tx = abs(this->get_x()-xcenter);  //x side of triangle
    int ty = abs(this->get_y()-ycenter);  //y side of triangle

    this->distance = sqrt((tx*tx) + (ty*ty));  //hypotenuse of triangle = our new radius

    this->xtheta = acos((this->get_x()-xcenter)/this->get_distance());  //theta at x
    this->ytheta = asin((this->get_y()-ycenter)/this->get_distance());  //theta at y
    this->speed = 1;
    int p = rand()%3+1;
    this->power = p;
    this->initpower = p;
    switch(p){
        case 1:
            this->size = 10;
            this->speed = 1.5;
            break;
        case 2:
            this->size = 15;
            this->speed = 1.25;
            break;
        case 3:
            this->size = 20;
            this->speed = 1;
            break;
    }
    if(powerup_count < max_powerup){
        if(rand()%3+1 == 1){
            this->powerup = rand()%10+1;
            powerup_count++;
        }
    }
}

void asteroid::move(ship* player, blackhole* hole){

    int shiphit = 0;

    if(this->power <= 0){
        this->exists = false;
        this->refresh();
    }
    else if (this->power == 1){
        this->size = 10;
        this->speed = 1.5;
    }
    else if (this->power == 2){
        this->size = 15;
        this->speed = 1.25;
    }
    else if (this->power == 3){
        this->size = 20;
        this->speed = 1.0;
    }
    if (this->distance > hole->get_size()+this->size ) {  //if has not hit edge of black hole (10 is cur black-hole-radius
        this->exists = true;
        this->distance -= this->speed*SCREEN_W*.0008;
        int tx = (int)(this->xtheta +this->ytheta / 2);
        this->x = (int)(this->distance*cos(this->xtheta)) + xcenter;
        this->y = (int)(this->distance*sin(this->ytheta)) + ycenter;
    }
    else if(this->distance <= hole->get_size()+this->size){               //blackhole eats ship
        this->exists = false;
        hole->set_size(hole->get_size() + (this->power));
        if(this->get_powerup()){
            powerup_count = 0;
            this->set_powerup(0);
        }
        if (hole->get_size() > player->get_distance()- player->get_size()){
            play_sample(crash_sound, 255, 128, 1000, 0);
            player->set_lives(player->get_lives() - 1);
            player->set_health(3);
        }
        this->refresh();
    }
//DETECT WHETHER SHIP COLLISION OCCURS-------------------------------------------
    if ( (!shiphit) &&((player->get_nx() > (this->x - this->size-2) &&
                        player->get_nx() < (this->x + this->size-2) &&
                        player->get_ny() > (this->y - this->size-2) &&
                        player->get_ny() < (this->y + this->size-2))
                    || (player->get_lx() > (this->x - this->size-2) &&
                        player->get_lx() < (this->x + this->size-2) &&
                        player->get_ly() > (this->y - this->size-2) &&
                        player->get_ly() < (this->y + this->size-2))
                    || (player->get_rx() > (this->x - this->size-2) &&
                        player->get_rx() < (this->x + this->size-2) &&
                        player->get_ry() > (this->y - this->size-2) &&
                        player->get_ry() < (this->y + this->size-2)) )) {
            shiphit = 1;
    }
//-------------------------------------------------------------------------------
    if(shiphit){
        player->set_health((player->get_health() - this->power));
        explode_gfx(this->x, this->y, this->size);
        play_sample(splode_sound, 255, 128, 1000, 0);
        if(this->get_powerup()){
            powerup_count = 0;
            this->set_powerup(0);
        }
        this->refresh();

        if(player->get_health() <= 0){
            explode_gfx(player->get_nx(), player->get_ny(), player->get_size());
            play_sample(crash_sound, 255, 128, 1000, 0);
            player->set_lives(player->get_lives() - 1);
            player->set_health(3);
        }
    }
}

void asteroid::draw(BITMAP* buffer){
    int color;
    int size;
    if(this->power == 1) {
        color = RED;
    }
    else if(this->power == 2) {
        color = YELLOW;
    }
    else if(this->power == 3) {
        color = GREEN;
    }
    else {
        color = WHITE;
    }
    circle(buffer, this->get_x(), this->get_y(), this->size, color);
    if(this->powerup){
        circle(buffer, this->get_x(), this->get_y(), this->size - 3, WHITE);
    }

}

void asteroid::refresh(){

    int r, rt;
    r = rand()%4;

    switch(r){
        case 0: this->y = 0;
        break;
        case 1: this->x = SCREEN_W;
        break;
        case 2: this->y = SCREEN_H;
        break;
        case 3: this->x = 0;
        break;
    }
    if((r == 0) || (r == 2)){
        this->x = rand()%SCREEN_W;
    }
    else if((r == 1) || (r == 3)){
        this->y = rand()%SCREEN_H;
    }
    int tx = abs(this->get_x()-xcenter);  //x side of triangle
    int ty = abs(this->get_y()-ycenter);  //y side of triangle

    this->distance = sqrt((tx*tx) + (ty*ty));  //hypotenuse of triangle = our new radius

    this->xtheta = acos((this->get_x()-xcenter)/this->get_distance());  //theta at x
    this->ytheta = asin((this->get_y()-ycenter)/this->get_distance());  //theta at y
    this->speed = 1;
    int p = rand()%3+1;
    this->power = p;
    switch(p){
        case 1:
            this->size = 10;
            this->speed = 1.5;
            break;
        case 2:
            this->size = 15;
            this->speed = 1.25;
            break;
        case 3:
            this->size = 20;
            this->speed = 1;
            break;
    }

    if(powerup_count < max_powerup){
        if(rand()%3+1 == 1){
            this->powerup = rand()%6+1;
            powerup_count++;
        }
    }
}

//BLACK HOLE-------------------------------------------------------------------------
blackhole::blackhole(){
    this->x = SCREEN_W/2;
    this->y = SCREEN_H/2;
    this->size = 10;
}

void blackhole::draw(){
    circle(buffer, this->x, this->y, this->size, PURPLE);
}

//------------------------------------
void powerup::move(ship* player, blackhole* hole){

    int shiphit = 0;
    if (this->distance > hole->get_size() ) {  //if has not hit edge of black hole
        this->distance -= this->speed;
        int tx = (int)(this->xtheta + this->ytheta / 2);
        this->x = (int)(this->distance*cos(this->xtheta)) + xcenter;
        this->y = (int)(this->distance*sin(this->ytheta)) + ycenter;
    }
    else if(this->distance <= hole->get_size()){                //blackhole eats ship
        this->refresh();
    }
//DETECT WHETHER SHIP COLLISION OCCURS-------------------------------------------
    if ( (!shiphit) &&((player->get_nx() > (this->x -10 ) &&
                        player->get_nx() < (this->x +10 ) &&
                        player->get_ny() > (this->y -10 ) &&
                        player->get_ny() < (this->y +10 ))
                    || (player->get_lx() > (this->x -10 ) &&
                        player->get_lx() < (this->x +10 ) &&
                        player->get_ly() > (this->y -10 ) &&
                        player->get_ly() < (this->y +10 ))
                    || (player->get_rx() > (this->x -10 ) &&
                        player->get_rx() < (this->x +10 ) &&
                        player->get_ry() > (this->y -10 ) &&
                        player->get_ry() < (this->y +10 )) )) {
            shiphit = 1;
    }
//-------------------------------------------------------------------------------
    if(shiphit){
        play_sample(pickup_sound, 255, 127, 1000, 0);
        powerup_count = 0;
        if(this->type == 1){
            player->set_health(3);
            this->refresh();
        }
        else if(this->type == 2){
            player->set_lives(player->get_lives()+1);
            this->refresh();
        }
        else if(this->type == 3){
            switch(player->get_cur_proj()){
                case 0:
                    if(bulletpower < 3){
                        bulletpower += 1;
                        this->refresh();
                        break;
                    }
                case 1:
                    if(spreadpower < 3){
                        spreadpower += 1;
                        this->refresh();
                        break;
                    }
            }
        }
        else if(this->type == 4){
            player->set_energy(player->get_energy()+1);
            this->refresh();
        }
        else if(this->type == 5){
            player->set_proj_count(1, 10);
            player->set_cur_proj(1);
            this->refresh();
        }
        else if(this->type == 6){
            player->set_proj_count(2, 10);
            player->set_cur_proj(2);
            this->refresh();
        }
    }
}
//-----------------------
void powerup::refresh(){
    powerup_count = 0;
    this->set_x(0);
    this->set_y(0);
    this->set_xtheta(0);
    this->set_ytheta(0);
    this->set_type(0);
    this->set_distance(5000);
    this->set_speed(0);
    this->set_exists(false);
}


//-----------------------------
void powerup::draw(){
    if(this->get_exists()){
        if (type == 1){                                                         // health
            point p[12];
            int size = 2;

            p[0].x = x - size;
            p[0].y = y - size*3;

            p[1].x = x + size;
            p[1].y = y - size*3;
            p[2].x = x + size;
            p[2].y = y - size;

            p[3].x = x + size*3;
            p[3].y = y - size;

            p[4].x = x + size*3;
            p[4].y = y + size;

            p[5].x = x + size;
            p[5].y = y + size;

            p[6].x = x + size;
            p[6].y = y + size*3;

            p[7].x = x - size;
            p[7].y = y + size*3;

            p[8].x = x - size;
            p[8].y = y + size;

            p[9].x = x - size*3;
            p[9].y = y + size;

            p[10].x = x - size*3;
            p[10].y = y - size;

            p[11].x = x - size;
            p[11].y = y- size;

            line(buffer, p[0].x, p[0].y, p[1].x, p[1].y, RED);
            line(buffer, p[1].x, p[1].y, p[2].x, p[2].y, RED);
            line(buffer, p[2].x, p[2].y, p[3].x, p[3].y, RED);
            line(buffer, p[3].x, p[3].y, p[4].x, p[4].y, RED);
            line(buffer, p[4].x, p[4].y, p[5].x, p[5].y, RED);
            line(buffer, p[5].x, p[5].y, p[6].x, p[6].y, RED);
            line(buffer, p[6].x, p[6].y, p[7].x, p[7].y, RED);
            line(buffer, p[7].x, p[7].y, p[8].x, p[8].y, RED);
            line(buffer, p[8].x, p[8].y, p[9].x, p[9].y, RED);
            line(buffer, p[9].x, p[9].y, p[10].x, p[10].y, RED);
            line(buffer, p[10].x, p[10].y, p[11].x, p[11].y, RED);
            line(buffer, p[11].x, p[11].y, p[0].x, p[0].y, RED);
        }
        else if (type == 2){                                                     //extra life
            point p[3];
            int size = 5;

            p[0].x = x;
            p[0].y = y - size;
            p[1].x = x + size;
            p[1].y = y + size;
            p[2].x = x - size;
            p[2].y = y + size;


            line(buffer, p[0].x, p[0].y, p[1].x, p[1].y, GREEN);
            line(buffer, p[1].x, p[1].y, p[2].x, p[2].y, GREEN);
            line(buffer, p[2].x, p[2].y, p[0].x, p[0].y, GREEN);

        }
        else if (type == 3){                                                    // weapon power-level up
            point p[5];
            int size = 6;

            p[0].x = x;
            p[0].y = y - size;
            p[1].x = x;
            p[1].y = y + size;
            p[2].x = x - size + 1;
            p[2].y = y - size + 5;
            p[3].x = x + size - 1;
            p[3].y = y - size + 5;
            p[4].x = p[0].x;
            p[4].y = p[1].y + size/2;

            line(buffer, p[0].x, p[0].y, p[1].x, p[1].y, GREEN);
            line(buffer, p[0].x, p[0].y, p[2].x, p[2].y, GREEN);
            line(buffer, p[0].x, p[0].y, p[3].x, p[3].y, GREEN);
            circle(buffer, p[4].x, p[4].y + size/2, size/2, GREEN);
        }
        else if (type == 4){                                                    //thrusters/energy
            rect(buffer, x-5, y-2, x+5, y+2, GREEN);
        }
        else if (type == 5){                                                    //spreadshot

            circle(buffer, x, y, 2, RED);
            circle(buffer, x-5, y+5, 2, RED);
            circle(buffer, x+5, y-5, 2, RED);
        }
        else if (type == 6){                                                    //laser
            line(buffer, x-5, y+5, x+5, y-5, RED);
        }
        else if (type == 7){                                                    //mines
            circle(buffer, x, y-5, 2, WHITE);
            circle(buffer, x+5, y, 2, WHITE);
            circle(buffer, x, y+5, 2, WHITE);
            circle(buffer, x-5, y, 2, WHITE);
        }
        else if (type == 8){                                                     //shield
            circle(buffer, x, y, 8, BLUE);
        }
        else if (type == 9){                                                     //remote detonaters
            circle(buffer, x-6, y, 1, YELLOW);
            circle(buffer, x-1, y, 2, ORANGE);
            circle(buffer, x+6, y, 3, RED);
        }

        else if (type == 10){                                                     //NUKE!!!
            point p[4];
            p[0].x = x-1;
            p[0].y = y;
            p[1].x = x+1;
            p[1].y = y;
            p[2].x = p[1].x;
            p[2].y = p[1].y + 2;
            p[3].x = x+3;
            p[3].y = y+5;
            p[4].x = x-3;
            p[4].y = y+5;
            p[5].x = p[0].x - 1;
            p[5].y = p[0].y + 2;

            line(buffer, p[0].x, p[0].y, p[1].x, p[1].y, RED);
            line(buffer, p[1].x, p[1].y, p[2].x, p[2].y, RED);
            line(buffer, p[2].x, p[2].y, p[3].x, p[3].y, RED);
            line(buffer, p[3].x, p[3].y, p[4].x, p[4].y, RED);
            line(buffer, p[4].x, p[4].y, p[5].x, p[5].y, RED);
            line(buffer, p[5].x, p[5].y, p[0].x, p[0].y, RED);
            circle(buffer, x, y-3, 3, RED);
        }
    }
}

